package cz.muni.fi.pa165.languageschool.web;

import cz.muni.fi.pa165.languageschool.DataFetchException;
import cz.muni.fi.pa165.languageschool.dto.LectureTO;
import cz.muni.fi.pa165.languageschool.dto.StudentTO;
import cz.muni.fi.pa165.languageschool.service.LectureService;
import cz.muni.fi.pa165.languageschool.service.StudentService;
import cz.muni.fi.pa165.languageschool.utils.Authority;
import java.util.List;
import net.sourceforge.stripes.action.Before;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.LocalizableMessage;
import net.sourceforge.stripes.action.RedirectResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.UrlBinding;
import net.sourceforge.stripes.controller.LifecycleStage;
import net.sourceforge.stripes.integration.spring.SpringBean;
import net.sourceforge.stripes.validation.LocalizableError;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidateNestedProperties;
import net.sourceforge.stripes.validation.ValidationErrorHandler;
import net.sourceforge.stripes.validation.ValidationErrors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;

/**
 * Action bean for handling with requests from students section
 *
 * @author Michal Fučík (395624) michal.fuca.fucik(at)gmail.com
 */
@UrlBinding("/students/{$event}/{activity.id}")
public class StudentActionBean extends BaseActionBean implements ValidationErrorHandler {

    final static Logger log = LoggerFactory.getLogger(StudentActionBean.class);

    @SpringBean
    protected StudentService studentService;
    @SpringBean
    protected LectureService lectureService;

    private List<StudentTO> students;
    private List<LectureTO> lectures;
    private List<LectureTO> enrolledLectures;

    private StudentTO student;
    private LectureTO lecture;

    private Long lectureId;

    public Authentication getAuth() {
        return auth;
    }

    public void setAuth(Authentication auth) {
        this.auth = auth;
    }

    Authentication auth;

    public LectureTO getLecture() {
        return lecture;
    }

    public void setLecture(LectureTO lecture) {
        this.lecture = lecture;
    }

    public Long getLectureId() {
        return lectureId;
    }

    public void setLectureId(Long lectureId) {
        this.lectureId = lectureId;
    }

    public void setEnrolledLectures(List<LectureTO> enrolledLectures) {
        this.enrolledLectures = enrolledLectures;
    }

    public List<LectureTO> getEnrolledLectures() {
        return enrolledLectures;
    }

    public List<LectureTO> getLectures() {
        return lectures;
    }

    public List<StudentTO> getStudents() {
        return students;
    }

    public StudentTO getStudent() {
        return student;
    }

    public void setLectures(List<StudentTO> students) {
        this.students = students;
    }

    public void setStudent(StudentTO student) {
        this.student = student;
    }

    @DefaultHandler
    public Resolution list() {
        log.info("Default handler students list");
        students = (List<StudentTO>) studentService.getAllStudents();
        return new ForwardResolution("/jsp/student/list.jsp");
    }

    @ValidateNestedProperties(value = {
        @Validate(on = {"addStudent", "saveStudent"}, field = "firstName", required = true),
        @Validate(on = {"addStudent", "saveStudent"}, field = "lastName", required = true),
        @Validate(on = {"addStudent", "saveStudent"}, field = "username", required = true),
        @Validate(on = {"addStudent", "saveStudent"}, field = "password", required = true),
        @Validate(on = {"addStudent", "saveStudent"}, field = "authority", required = true),})

    public Resolution addStudent() {
        student.setEnabled(true);
        student.setAuthority(Authority.ROLE_USER);
        log.debug("add() student={}", student);
        studentService.createStudent(student);
        getContext().getMessages().add(new LocalizableMessage("student.add.success", escapeHTML(student.getFirstName() + " " + student.getLastName()), escapeHTML(student.getFirstName() + " " + student.getLastName())));
        return new RedirectResolution(this.getClass(), "list");
    }

    @Override
    public Resolution handleValidationErrors(ValidationErrors errors) throws Exception {
        students = (List<StudentTO>) studentService.getAllStudents();
        return null;
    }

    public Resolution delete() {
        log.debug("delete({})", student.getId());
        student = studentService.getStudent(student.getId());
        studentService.deleteStudent(student);
        getContext().getMessages().add(new LocalizableMessage("student.delete.success", escapeHTML(student.getFirstName() + " " + student.getLastName()), escapeHTML(student.getFirstName() + " " + student.getLastName())));
        return new RedirectResolution(this.getClass(), "list");
    }

    @Before(stages = LifecycleStage.BindingAndValidation, on = {"edit", "save"})
    public void loadStudentFromDatabase() {
        log.debug("LOADING student={}", student);
        String ids = getContext().getRequest().getParameter("student.id");
        if (ids == null) {
            return;
        }
        student = studentService.getStudent(Long.parseLong(ids));
    }

    public Resolution edit() {
        log.debug("EDIT student={}", student);

        return new ForwardResolution("/jsp/student/edit.jsp");
    }

    public Resolution saveStudent() {
        student.setEnabled(true);
        student.setAuthority(Authority.ROLE_USER);
        log.debug("UPDATING student={}", student);

        studentService.updateStudent(student);
        return new RedirectResolution(this.getClass(), "list");
    }

    public Resolution cancelEdit() {
        log.info("Student editing cancelled");
        return new RedirectResolution(this.getClass(), "list");
    }

    public Resolution detail() {
        String id = getContext().getRequest().getParameter("student.id");
        if (id == null) {
            return new ForwardResolution(this.getClass(), "list");
        }
        student = studentService.getStudent(Long.parseLong(id));
        log.debug("detail student={}", student);

        lectures = (List<LectureTO>) lectureService.getAllLectures();
        enrolledLectures = student.getEnrolledLectures();

        return new ForwardResolution("/jsp/student/detail.jsp");
    }

    public Resolution enroll() {
        String id = getContext().getRequest().getParameter("student.id");
        if (id == null) {
            return new ForwardResolution(this.getClass(), "list");
        }
        try {
            student = studentService.getStudent(Long.parseLong(id));
            lecture = lectureService.getLecture(lectureId);
        } catch (DataFetchException | NullPointerException x) {
            this.getContext().getMessages().add(new LocalizableError("student.enroll.failure.lower"));
            return new RedirectResolution("/students/detail?student.id=" + student.getId());
        }
        try {
            lectureService.enrollStudent(lecture, student);
        } catch (IllegalStateException x) {
            this.getContext().getMessages().add(new LocalizableError("student.enroll.failure.enrolled",
                    escapeHTML(lecture.getLabel()) + " - " + escapeHTML(lecture.getCourse().getName())));
            return new RedirectResolution("/students/detail?student.id=" + student.getId());
        }
        return new RedirectResolution("/students/detail?student.id=" + student.getId());
    }

    public Resolution redirectBack() {
        return new RedirectResolution(this.getClass(), "list");
    }

    public Resolution disenroll() {
        log.debug("disenrol student={} from lecture={}", student, lecture);
        String ids = getContext().getRequest().getParameter("student.id");
        String idl = getContext().getRequest().getParameter("lecture.id");
        if (ids == null || idl == null) {
            return new RedirectResolution(this.getClass(), "detail");
        }
        try {
            student = studentService.getStudent(Long.parseLong(ids));
            lecture = lectureService.getLecture(Long.parseLong(idl));
            lectureService.disenrollStudent(lecture, student);
        } catch (DataFetchException | NullPointerException x) {
            this.getContext().getMessages().add(new LocalizableError("student.disenroll.failure.lower",
                    escapeHTML(lecture.getLabel()) + " - " + escapeHTML(lecture.getCourse().getName())));
            return new RedirectResolution("/students/detail?student.id=" + student.getId());
        }
        return new RedirectResolution("/students/detail?student.id=" + student.getId());
    }

}
